﻿#include "ArkXbpPool.h"

namespace kd
{
	ArkXbpPool::ArkXbpPool(const ArkKcxpSvr& xpSvrinfo, const int32_t nPoolsize)
	{
		mkMaxConns = nPoolsize;
		mXpServer = std::move(xpSvrinfo);
	}

	ArkXbpPool::~ArkXbpPool()
	{
		std::list<ArkXbpClient*>::iterator it = mkClientList.begin();
		for (; it != mkClientList.end(); ++it) {
			delete *it;
		}
	}

	ArkXbpClient* ArkXbpPool::PeekClient(int& errcode, std::string& errDesc)
	{
		//线程安全锁
		ARK_LOCK_GUARD(mkLocker);

		if (mkbIsAlive == false)
		{
			errcode = -3;
			time_t now = time(NULL);
			// 重连间隔小于0，直接返回空
			if (mkRetryInter <= 0)
				return NULL;
			// 上次连接和本次连接时间间隔太短
			if (now - mkLastDead < mkRetryInter) {
				//std::ostringstream ssDesc;
				//ssDesc << "KCXP连接失败频繁，等待" << mkiRetryInter << "秒后尝试重连...";
				//errDesc = ssDesc.str();
				return NULL;
			}
			// 重置服务端连接状态，以便重试
			mkbIsAlive = true;
		}

		errcode = 0;
		ArkXbpClient* kcxpConn = nullptr;
		std::list<ArkXbpClient*>::iterator it = mkClientList.begin();
		if (it != mkClientList.end())
		{
			kcxpConn = *it;
			mkClientList.erase(it);
			mkTotalUsed++;

			return kcxpConn;
		}
		else if (mkMaxConns > 0 && mkConCount >= mkMaxConns)
		{
			errcode = -1;
			std::ostringstream ssDesc;
			ssDesc << "KCXP connection pool upper limit[" << mkMaxConns << "]，current connection index[" << mkConCount << "]";
			errDesc = ssDesc.str();
			return NULL;
		}

		// 将以下三个值预 +1
		mkConCount++;
		mkTotalUsed++;

		// 调用虚函数的子类实现方法，创建新连接对象，并打开连接
		kcxpConn = CreateConnect();
		if (!kcxpConn) return NULL;

		// 调用子类方法打开连接
		if (0 != kcxpConn->Connect())
		{
			// 因为打开连接失败，所以还需将上面预 +1 的三个成员再 -1
			mkConCount--;
			mkTotalUsed--;

			errcode = -2;
			mkbIsAlive = false;
			(void)time(&mkLastDead);

			// 获取错误描述信息
			errDesc = kcxpConn->GetLastErrorInfo();

			delete kcxpConn;
			return NULL;
		}

		return kcxpConn;
	}

	void ArkXbpPool::PutClient(ArkXbpClient* conn, bool keep /* = true */)
	{
		time_t now = time(NULL);

		//线程安全锁
		ARK_LOCK_GUARD(mkLocker);

		// 检查是否设置了自销毁标志位
		if (mkbDelayDestroy)
		{
			delete conn;
			mkConCount--;

			// 如果引用计数为 0 则自销毁
			if (mkConCount <= 0)
				delete this;
			return;
		}

		if (keep && mkbIsAlive)
		{
			conn->SetTimeStamp(now);
			// 将归还的连接放在链表首部，这样在调用释放过期连接
			// 时比较方便，有利于尽快将不忙的数据库连接关闭
			mkClientList.emplace_front(conn);
		}
		else
		{
			assert(mkConCount > 0 && "mkCount");
			delete conn;
			mkConCount--;
		}

		if (mkIdlettl >= 0 && now - mkLastCheck >= mkCheckInter)
		{
			(void)CheckIdle(mkIdlettl, false);
			(void)time(&mkLastCheck);
		}
	}

	ArkXbpClient* ArkXbpPool::CreateConnect()
	{
		ArkXbpClient* kcxpClient = new ArkXbpClient(mXpServer);
		if (!kcxpClient) return NULL;

		kcxpClient->SetMaxWaitTimes(mkWaitTimes);

		return kcxpClient;
	}

	int ArkXbpPool::CheckIdle(time_t ttl, bool exclusive)
	{
		if (ttl < 0) return 0;
		if (exclusive) mkLocker.lock();

		if (mkClientList.empty()) {
			if (exclusive) mkLocker.unlock();
			return 0;
		}

		if (ttl == 0)
		{
			int   n = 0;
			std::list<ArkXbpClient*>::iterator it = mkClientList.begin();
			for (; it != mkClientList.end(); ++it)
			{
				delete *it;
				n++;
			};

			mkClientList.clear();
			mkConCount = 0;
			if (exclusive) mkLocker.unlock();
			return n;
		}

		int n = 0;
		time_t now = time(NULL), when;
		std::list<ArkXbpClient*>::iterator it, next;
		std::list<ArkXbpClient*>::reverse_iterator rit = mkClientList.rbegin();

		for (; rit != mkClientList.rend();)
		{
			it = --rit.base();
			when = (*it)->GetTimeStamp();
			if (when <= 0) {
				++rit;
				continue;
			}

			if (now - when < ttl)
				break;

			delete *it;
			next = mkClientList.erase(it);
			rit = std::list<ArkXbpClient*>::reverse_iterator(next);

			n++;
			mkConCount--;
		}

		if (exclusive) mkLocker.unlock();
		return n;
	}

	ArkXbpPool& ArkXbpPool::SetWaitTimes(int times)
	{
		ARK_LOCK_GUARD(mkLocker);
		mkWaitTimes = times;
		return *this;
	}

	ArkXbpPool& ArkXbpPool::SetIdlettl(time_t ttl)
	{
		ARK_LOCK_GUARD(mkLocker);
		mkIdlettl = ttl;
		return *this;
	}

	ArkXbpPool& ArkXbpPool::SetPoolAttr(bool bIsRequest)
	{
		ARK_LOCK_GUARD(mkLocker);
		mkRequest = bIsRequest;
		return *this;
	}

	void ArkXbpPool::SetDelayDestroy()
	{
		ARK_LOCK_GUARD(mkLocker);
		mkbDelayDestroy = true;
	}
}